import sys
import os
import serial
import serial.tools.list_ports
from PyQt5 import  QtWidgets
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
                             QLabel, QComboBox, QPushButton, QWidget, QGroupBox,
                             QLCDNumber, QProgressBar)
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QFont,QIcon

from utils import resource_path
image_path = resource_path("img/")

class SerialMonitor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.serial_port = None
        self.lut = []
        self.init_ui()
        self.init_crc_table()
        self.refresh_ports()
        
    def init_ui(self):
        self.setWindowTitle("CRSF通道数据串口显示工具 V1.0.0")
        self.setWindowIcon(QIcon(image_path+'chanel.ico'))  # 设置窗口图标
        screen_size = QtWidgets.QDesktopWidget().screenGeometry()
        P_screen_w = int(0.5*screen_size.width())
        P_screen_h = int(0.5*screen_size.height())
        self.resize(P_screen_w, P_screen_h)
        
        # 主部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        
        # 主布局
        main_layout = QVBoxLayout()
        
        # 串口配置区域
        config_group = QGroupBox("串口配置")
        config_layout = QHBoxLayout()
        
        self.port_label = QLabel("端口:")
        self.port_combo = QComboBox()
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.refresh_ports)
        
        self.baud_label = QLabel("波特率:")
        self.baud_combo = QComboBox()
        self.baud_combo.addItems(["9600", "19200", "38400", "57600", "115200", "230400", "420000", "460800"])
        self.baud_combo.setCurrentText("420000")
        
        self.connect_btn = QPushButton("连接")
        self.connect_btn.clicked.connect(self.toggle_connection)
        
        config_layout.addWidget(self.port_label)
        config_layout.addWidget(self.port_combo)
        config_layout.addWidget(self.refresh_btn)
        config_layout.addWidget(self.baud_label)
        config_layout.addWidget(self.baud_combo)
        config_layout.addWidget(self.connect_btn)
        config_group.setLayout(config_layout)
        
        # 数据显示区域
        data_group = QGroupBox("通道数据")
        data_layout = QVBoxLayout()
        
        # 创建16个通道的显示
        self.channels = []
        for i in range(16):
            channel_layout = QHBoxLayout()
            
            label = QLabel(f"通道 {i+1}:")
            label.setFixedWidth(60)
            
            lcd = QLCDNumber()
            lcd.setDigitCount(16)
            lcd.setSegmentStyle(QLCDNumber.Flat)
            lcd.setFixedWidth(100)
            
            progress = QProgressBar()
            progress.setRange(0, 2047)
            progress.setValue(0)
            progress.setTextVisible(False)
            
            channel_layout.addWidget(label)
            channel_layout.addWidget(lcd)
            channel_layout.addWidget(progress)
            
            self.channels.append((lcd, progress))
            data_layout.addLayout(channel_layout)
        
        data_group.setLayout(data_layout)
        
        # 添加到主布局
        main_layout.addWidget(config_group)
        main_layout.addWidget(data_group)
        
        main_widget.setLayout(main_layout)
        
        # 定时器用于读取串口数据
        self.timer = QTimer()
        self.timer.timeout.connect(self.read_serial_data)
        
        # 状态栏
        self.status_bar = self.statusBar()
        self.status_bar.showMessage("就绪")
    
    def init_crc_table(self):
        """初始化CRC校验表"""
        poly = 0xD5
        idx = 0
        self.lut = [0] * 256
        while idx < 256:
            crc = idx
            shift = 0
            while shift < 8:
                shift += 1
                crc_temp = poly if (crc & 0x80) else 0
                crc = (crc << 1) ^ crc_temp
            self.lut[idx] = crc & 0xff
            idx += 1
    
    def refresh_ports(self):
        """刷新可用串口列表"""
        self.port_combo.clear()
        ports = serial.tools.list_ports.comports()
        for port in ports:
            self.port_combo.addItem(port.device)  # 只添加设备名称，避免后续解析问题
    
    def toggle_connection(self):
        """连接/断开串口"""
        if self.serial_port and self.serial_port.is_open:
            self.disconnect_serial()
        else:
            self.connect_serial()
    
    def connect_serial(self):
        """连接串口"""
        if not self.port_combo.currentText():
            self.status_bar.showMessage("请选择串口")
            return
            
        port_name = self.port_combo.currentText()  # 直接获取设备名称
        baud_rate = int(self.baud_combo.currentText())
        
        try:
            self.serial_port = serial.Serial(
                port=port_name,
                baudrate=baud_rate,
                bytesize=8,
                parity='N',
                stopbits=1,
                timeout=1.0
            )
            self.connect_btn.setText("断开")
            self.status_bar.showMessage(f"已连接 {port_name} @ {baud_rate} bps")
            self.timer.start(10)  # 每10ms读取一次数据
        except Exception as e:
            self.status_bar.showMessage(f"连接失败: {str(e)}")
    
    def disconnect_serial(self):
        """断开串口连接"""
        if self.serial_port and self.serial_port.is_open:
            self.timer.stop()
            self.serial_port.close()
            self.connect_btn.setText("连接")
            self.status_bar.showMessage("已断开连接")
    
    def read_serial_data(self):
        """读取并处理串口数据"""
        if not self.serial_port or not self.serial_port.is_open:
            return
            
        data = self.serial_port.read_all()
        if not data:
            return
            
        try:
            # 检查数据长度和CRC校验
            if len(data) < 3:
                return
                
            len_data = data[1]  # 假设第二个字节是数据长度0
            if len(data) < (len_data + 3):  # 有效数据长度 + 1（长度字节） + 1（数据包类型字节） + 1（CRC字节）
                return
                
            data_idx = 2
            inCrc = data[2 + len_data - 1]
            crc = 0
            temp_len = len_data - 1
            
            while temp_len > 0:
                crc = self.lut[crc ^ data[data_idx]]
                data_idx += 1
                temp_len -= 1
                
            if (data[2] == 0x16) and (len(data) >= (len_data + 2)) and (inCrc == crc):    
                # 解析通道数据
                ch1 = ((data[3] >> 0) | (data[4] << 8)) & 0x07FF
                ch2 = ((data[4] >> 3) | (data[5] << 5)) & 0x07FF
                ch3 = ((data[5] >> 6) | (data[6] << 2) | (data[7] << 10)) & 0x07FF
                ch4 = ((data[7] >> 1) | (data[8] << 7)) & 0x07FF
                ch5 = ((data[8] >> 4) | (data[9] << 4)) & 0x07FF
                ch6 = ((data[9] >> 7) | (data[10] << 1) | (data[11] << 9)) & 0x07FF
                ch7 = ((data[11] >> 2) | (data[12] << 6)) & 0x07FF
                ch8 = ((data[12] >> 5) | (data[13] << 3)) & 0x07FF
                ch9 = ((data[14] << 0) | (data[15] << 8 )) & 0x07FF
                ch10 = ((data[15] >> 3) | (data[16] << 5 )) & 0x07FF
                ch11 = ((data[16] >> 6) | (data[17] << 2 ) | data[18] << 10 ) & 0x07FF
                ch12 = ((data[18] >> 1) | (data[19] << 7 )) & 0x07FF

                ch13 = ((data[19] >> 4) | (data[20] << 4 )) & 0x07FF
                ch14 = ((data[20] >> 7) | (data[21] << 1 ) | data[22] << 9 ) & 0x07FF
                ch15 = ((data[22] >> 2) | (data[23] << 6 )) & 0x07FF
                ch16 = ((data[23] >> 5) | (data[24] << 3 )) & 0x07FF


                
                # 更新UI显示
                self.update_channel_display(0, ch1)
                self.update_channel_display(1, ch2)
                self.update_channel_display(2, ch3)
                self.update_channel_display(3, ch4)
                self.update_channel_display(4, ch5)
                self.update_channel_display(5, ch6)
                self.update_channel_display(6, ch7)
                self.update_channel_display(7, ch8)
                self.update_channel_display(8, ch9)
                self.update_channel_display(9, ch10)
                self.update_channel_display(10, ch11)
                self.update_channel_display(11, ch12)
                self.update_channel_display(12, ch13)
                self.update_channel_display(13, ch14)
                self.update_channel_display(14, ch15)
                self.update_channel_display(15, ch16)
                
        except Exception as e:
            self.status_bar.showMessage(f"数据处理错误: {str(e)}")
    
    def update_channel_display(self, channel_idx, value):
        """更新通道数据显示"""
        lcd, progress = self.channels[channel_idx]
        lcd.display(value)
        progress.setValue(value)
        
        # 根据值设置进度条颜色
        if value > 1800:
            progress.setStyleSheet("QProgressBar::chunk { background-color: red; }")
        elif value > 992:
            progress.setStyleSheet("QProgressBar::chunk { background-color: orange; }")
        else:
            progress.setStyleSheet("QProgressBar::chunk { background-color: green; }")
    
    def closeEvent(self, event):
        """窗口关闭事件处理"""
        self.disconnect_serial()
        event.accept()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    monitor = SerialMonitor()
    monitor.show()
    sys.exit(app.exec_())
